/* ************************************* */
/* * Main Interface for PI Algorithms  * */
/* ************************************* */

#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* fgets buffer size */
#define BUFFER_SIZE 258

/* Monte Carlo option */
inline void monte_carlo(void) {
	char sel2, buffer[BUFFER_SIZE], **argv;

	printf("\tSequential (1) or Parallel (2)? ");

	/* Pick which version */
	sel2 = getchar();
	while('\n' != getchar());

	switch(sel2) {
		/* Sequential */
		case '1':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./monteCarloSeq")+1)*sizeof(char));
				strcpy(argv[3], "./monteCarloSeq");

				/* Get number of iterations */
				printf("\t\tEnter number of iterations: ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put number of iterations as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute monteCarloSeq */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
		/* Parallel */
		case '2':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(7*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./monteCarloPar")+1)*sizeof(char));
				strcpy(argv[3], "./monteCarloPar");

				/* Get number of iterations */
				printf("\t\tEnter number of iterations: ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put number of iterations as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);

				/* Get number of threads */
				printf("\t\tEnter number of threads: ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv[4]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv[4]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put number of threads as an argument */
				argv[5] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[5], buffer);
				argv[6] = NULL;

				break;
			}

			/* Try to execute monteCarloPar */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv[5]);
				free(argv);
				exit(1);
			}
			break;
	}

	/* Invalid option was set, return to main menu */
	printf("\tInvalid option!\n");
}

/* Gauss-Legendre option */
inline void gauss_legendre(void) {
	char sel3, buffer[BUFFER_SIZE], **argv;

	printf("\tSequential (1), Parallel - 2 Threads (2) or Parallel - 4 Threads (3)? ");

	/* Pick which version */
	sel3 = getchar();
	while('\n' != getchar());

	switch(sel3) {
		/* Sequential */
		case '1':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./gaussSeq")+1)*sizeof(char));
				strcpy(argv[3], "./gaussSeq");

				/* Get precision */
				printf("\t\tEnter mantissa precision (in bits): ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put precision as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute gaussSeq */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
		/* Parallel 2 Threads */
		case '2':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./gauss2Par")+1)*sizeof(char));
				strcpy(argv[3], "./gauss2Par");

				/* Get precision */
				printf("\t\tEnter mantissa precision (in bits): ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put precision as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute gauss2Par */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
		/* Parallel 4 Threads */
		case '3':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./gauss4Par")+1)*sizeof(char));
				strcpy(argv[3], "./gauss4Par");

				/* Get precision */
				printf("\t\tEnter mantissa precision (in bits): ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put precision as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute gauss4Par */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
	}

	/* Invalid option was set, return to main menu */
	printf("\tInvalid option!\n");
}

/* Borwein option */
inline void borwein(void) {
	char sel4, buffer[BUFFER_SIZE], **argv;

	printf("\tSequential (1) or Parallel (2)? ");

	/* Pick which version */
	sel4 = getchar();
	while('\n' != getchar());

	switch(sel4) {
		/* Sequential */
		case '1':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./borweinSeq")+1)*sizeof(char));
				strcpy(argv[3], "./borweinSeq");

				/* Get precision */
				printf("\t\tEnter mantissa precision (in bits): ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put precision as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute borweinSeq */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
		/* Parallel */
		case '2':
			while(1) {
				/* Malloc first arguments for execv */
				argv = malloc(6*sizeof(char *));
				argv[0] = malloc((strlen("/usr/bin/time")+1)*sizeof(char));
				strcpy(argv[0], "/usr/bin/time");
				argv[1] = malloc((strlen("-f")+1)*sizeof(char));
				strcpy(argv[1], "-f");
				argv[2] = malloc((strlen("Time: %e seconds")+1)*sizeof(char));
				strcpy(argv[2], "Time: %e seconds");
				argv[3] = malloc((strlen("./borweinPar")+1)*sizeof(char));
				strcpy(argv[3], "./borweinPar");

				/* Get precision */
				printf("\t\tEnter mantissa precision (in bits): ");
				if(NULL == fgets(buffer, BUFFER_SIZE, stdin)) {
					/* Something went wrong, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					printf("\t\tError getting input\n");
					continue;
				}
				else if(BUFFER_SIZE-1 == strlen(buffer) && '\n' != buffer[BUFFER_SIZE-2]) {
					/* Input string too big, free everything and try again */
					free(argv[0]);
					free(argv[1]);
					free(argv[2]);
					free(argv[3]);
					free(argv);

					/* Eat the rest of the input string */
					while('\n' != getchar());
					printf("\t\tInput exceeded buffer\n");
					continue;
				}

				/* Put precision as an argument */
				argv[4] = malloc((strlen(buffer)+1)*sizeof(char));
				strcpy(argv[4], buffer);
				argv[5] = NULL;

				break;
			}

			/* Try to execute borweinPar */
			if(execv(argv[0], argv) < 0) {
				/* Something went wrong, report error, free everything and quit */
				fprintf(stderr, "Error: %s: %s\n", strerror(errno), argv[0]);

				free(argv[0]);
				free(argv[1]);
				free(argv[2]);
				free(argv[3]);
				free(argv[4]);
				free(argv);
				exit(1);
			}
			break;
	}

	/* Invalid option was set, return to main menu */
	printf("\tInvalid option!\n");
}

int main(void) {
	int sel, loop = true;

	while(loop) {
		printf("Hello! Pick one of the options below for calculating PI!\n\n");
		printf("1. Monte Carlo\n2. Gauss-Legendre\n3. Borwein\nq. Quit\n\nChoice: ");

		/* Select option */
		sel = getchar();
		while('\n' != getchar());

		switch(sel) {
			/* Monte Carlo option */
			case '1':
				monte_carlo();
				break;
			/* Gauss-Legendre option */
			case '2':
				gauss_legendre();
				break;
			/* Borwein option */
			case '3':
				borwein();
				break;
			/* Quit option */
			case 'q':
				loop = false;
				break;
		}
	}

	printf("Bye!\n");

	return 0;
}